package com.oig.sys.base.service.impl;

import cn.hutool.core.util.StrUtil;
import com.oig.common.constants.CommonConstants;
import com.oig.common.constants.CommonEnum;
import com.oig.common.exception.BadRequestException;
import com.oig.common.module.SysRequest;
import com.oig.sys.base.constants.SysBaseEnum;
import com.oig.sys.base.dao.ITbAreaDao;
import com.oig.sys.base.dao.ITbCountryDao;
import com.oig.sys.base.dto.AreaDto;
import com.oig.sys.base.dto.AreaJsonDto;
import com.oig.sys.base.entity.TbArea;
import com.oig.sys.base.entity.TbCountry;
import com.oig.sys.base.service.ITbAreaService;
import com.oig.sys.base.vo.AreaVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @author wuxl
 */
@Slf4j
@Service
public class TbAreaServiceImpl implements ITbAreaService {

	private final ITbAreaDao tbAreaDao;
	private final ITbCountryDao countryDao;

	@Autowired
	public TbAreaServiceImpl(ITbAreaDao tbAreaDao,ITbCountryDao countryDao){
		 this.tbAreaDao = tbAreaDao;
		 this.countryDao = countryDao ;
	}

	@Transactional
	@Override
	public AreaVo addArea(AreaDto areaDto) {
	    TbArea area = new TbArea();
        BeanUtils.copyProperties(areaDto,area);
        area.setStatus(CommonEnum.CommonStatusEnum.OK.getCode());
	    if (areaDto.getParentId()!=null && areaDto.getParentId()>0){
            TbArea parent = this.tbAreaDao.getTbAreaById(area.getParentId()) ;
            if (parent==null){
                throw new BadRequestException("上级id不存在");
            }
            if (SysBaseEnum.GradeType.AREA.getCode().equals(parent.getGrade())){
                throw new BadRequestException("最多加4级行政区划");
            }
	        parent.setSubIndex(parent.getSubIndex()+1);
	        this.tbAreaDao.save(parent);
	        area.setCountryCode(parent.getCountryCode());
	        area.setGrade(parent.getGrade()+1);
	        area.setFullName(parent.getFullName()+area.getName());
	        area.setTreePath(parent.getTreePath() + parent.getId() + CommonConstants.DOT_STR);
        } else {
            TbCountry country = this.countryDao.getByCode(areaDto.getCode());
            if (country==null){
                throw new BadRequestException("编码不对，不是国家或地区");
            }
            TbArea countryArea = this.tbAreaDao.getByCode(country.getCode());
            if (countryArea!=null){
                throw new BadRequestException("编码不对，国家地区行政区划已存在") ;
            }
            area.setCountryCode(country.getCode());
            area.setGrade(SysBaseEnum.GradeType.COUNTRY.getCode());
            area.setParentId(0);
            area.setFullName(area.getName());
            area.setTreePath(CommonConstants.DOT_STR);
        }
	    area.setSubIndex(0);
        this.tbAreaDao.save(area) ;
        AreaVo areaVo = new AreaVo();
        BeanUtils.copyProperties(area, areaVo);
		return areaVo;
	}

	@Transactional
	@Override
	public AreaVo updateArea(AreaDto areaDto) {
        TbArea area = this.tbAreaDao.getTbAreaById(areaDto.getId()) ;
        if (area==null){
            throw new BadRequestException("error id") ;
        }
        //只改名称编码
        area.setCode(areaDto.getCode());
        area.setName(areaDto.getName());
        area.setEnName(areaDto.getEnName());
        this.tbAreaDao.save(area);
        AreaVo areaVo = new AreaVo();
        BeanUtils.copyProperties(area, areaVo);
        return areaVo;
	}

	@Transactional
	@Override
	public void cancelArea(Integer id) {
        TbArea area = this.tbAreaDao.getTbAreaById(id) ;
        if (area==null){
            throw new BadRequestException("error id") ;
        }
        area.setStatus(CommonEnum.CommonStatusEnum.DEL.getCode());
        this.tbAreaDao.save(area) ;
	}

	private TbArea updateParentSubIndex(String parentCode){
        TbArea parent = this.tbAreaDao.getByCode(parentCode);
        if (parent==null){
            throw new BadRequestException("所在区不存在") ;
        }
        parent.setSubIndex(parent.getSubIndex()+1);
        this.tbAreaDao.save(parent);
        return parent;
    }

    private TbArea addCountryArea(TbCountry country){
        TbArea area = new TbArea();
        area.setCountryCode(country.getCode());
        area.setStatus(CommonEnum.CommonStatusEnum.OK.getCode());
        area.setCode(country.getCode());
        area.setName(country.getName());
        area.setGrade(SysBaseEnum.GradeType.COUNTRY.getCode());
        area.setSubIndex(0);
        area.setParentId(0);
        area.setFullName(area.getName());
        area.setTreePath(CommonConstants.DOT_STR);
        this.tbAreaDao.save(area) ;
        return area;
    }

    @Override
    public AreaVo findAreaByCode(String code) {
        TbArea area = this.tbAreaDao.getByCode(code);
        if (area==null){
            return null;
        }
        AreaVo areaVo = new AreaVo();
        BeanUtils.copyProperties(area, areaVo);
        return areaVo ;
    }

    @Override
    public AreaVo findAreaByName(String name) {
	    TbArea area = this.tbAreaDao.getByName(name);
	    if (area==null){
            return null;
        }
        AreaVo areaVo = new AreaVo();
        BeanUtils.copyProperties(area, areaVo);
	    return areaVo ;
    }

    @Override
    public List<AreaVo> findAreaByCodes(List<String> codeList) {
        List<TbArea> areaList = tbAreaDao.findAreaByCodeList(codeList);
        List<AreaVo> areaVoList = new ArrayList<>();
        if (areaList!=null){
            areaList.forEach((area)->{
                AreaVo areaVo = new AreaVo();
                BeanUtils.copyProperties(area, areaVo);
                areaVoList.add(areaVo);
            });
        }
        return areaVoList;
    }

    @Override
    public List<AreaVo> listArea(Integer parentId) {
        return this.tbAreaDao.findByParentId(parentId);
    }

    @Transactional
    @Override
    public List<AreaVo> addAllArea(String countryCode, List<AreaJsonDto> areaJsonDtoList) {
        TbCountry country = this.countryDao.getByCode(countryCode) ;
        if (country==null || !SysBaseEnum.AreaType.COUNTRY.getCode().equals(country.getAreaType())){
            throw new BadRequestException("所属国家编码错误") ;
        }
        TbArea countryArea = this.tbAreaDao.getByCode(countryCode) ;
        if (countryArea==null){
            countryArea = this.addCountryArea(country);
        }
	    List<AreaVo> areaVoList = new ArrayList<>();
	    for (AreaJsonDto areaJsonDto : areaJsonDtoList){
            TbArea area = this.tbAreaDao.getByCode(areaJsonDto.getCode());
            if (area!=null){
                continue;
            }
            area = new TbArea();
	        area.setCountryCode(countryCode);
	        area.setStatus(CommonEnum.CommonStatusEnum.OK.getCode());
	        area.setCode(areaJsonDto.getCode());
	        area.setName(areaJsonDto.getName());
	        area.setSubIndex(0);
            TbArea parent = null;
            if (StringUtils.isNotBlank(areaJsonDto.getAreaCode())){ //区代码不为空，代表这条数据是街道
                parent = this.updateParentSubIndex(areaJsonDto.getAreaCode()) ;
            } else if (StringUtils.isNotBlank(areaJsonDto.getCityCode())){  //上级是市，代表本条数据是区
                parent = this.updateParentSubIndex(areaJsonDto.getCityCode()) ;
            } else if (StringUtils.isNotBlank(areaJsonDto.getProvinceCode())){
                parent = this.updateParentSubIndex(areaJsonDto.getProvinceCode()) ;
            }
            if (parent==null){
                countryArea.setSubIndex(countryArea.getSubIndex()+1);
                this.tbAreaDao.save(countryArea);
                parent = countryArea ;
            }
            area.setGrade(parent.getGrade()+1);
            area.setParentId(parent.getId());
            area.setFullName(parent.getFullName() + area.getName());
            area.setTreePath(parent.getTreePath() + parent.getId() + CommonConstants.DOT_STR);
            this.tbAreaDao.save(area) ;
            AreaVo areaVo = new AreaVo();
            BeanUtils.copyProperties(area, areaVo);
            areaVoList.add(areaVo);
        }
        return areaVoList;
    }

    @Override
	public List<AreaVo> queryArea(SysRequest<AreaDto> areaRequest) {
        Pageable pageable = areaRequest.pageable();
        Page<TbArea> areaPage ;
        if (areaRequest.getParamDto()!=null){
            Specification<TbArea> specification = new Specification<TbArea>() {
                @Override
                @Nullable
                public Predicate toPredicate(@NonNull Root<TbArea> root, @NonNull CriteriaQuery<?> criteriaQuery, @NonNull CriteriaBuilder criteriaBuilder) {
                    List<Predicate> predicateList = new ArrayList<>();
                    if (StrUtil.isNotBlank(areaRequest.getParamDto().getCountryCode())) {
                        Path<String> codePath = root.get("countryCode");
                        predicateList.add(criteriaBuilder.equal(codePath, areaRequest.getParamDto().getCountryCode()));
                    }
                    if (areaRequest.getParamDto().getParentId()!=null) {
                        Path<String> codePath = root.get("parentId");
                        predicateList.add(criteriaBuilder.equal(codePath, areaRequest.getParamDto().getParentId()));
                    }
                    Path<String> statusPath = root.get("status");
                    if (StrUtil.isNotBlank(areaRequest.getParamDto().getStatus())) {
                        predicateList.add(criteriaBuilder.equal(statusPath, areaRequest.getParamDto().getStatus()));
                    } else {
                        log.debug("def query ok status");
                        predicateList.add(criteriaBuilder.equal(statusPath, CommonEnum.CommonStatusEnum.OK.getCode()));
                    }
                    if (StrUtil.isNotBlank(areaRequest.getParamDto().getName())) {
                        Path<String> namePath = root.get("name");
                        predicateList.add(criteriaBuilder.like(namePath, "%" + areaRequest.getParamDto().getName() + "%"));
                    }
                    criteriaQuery.where(predicateList.toArray(new Predicate[0]));
                    return criteriaQuery.getRestriction();
                }
            } ;
            areaPage = this.tbAreaDao.findAll(specification,pageable);
        } else {
            areaPage = this.tbAreaDao.findAll(pageable);
        }
        List<AreaVo> areaVoList = new ArrayList<>();
        for (TbArea area : areaPage.getContent()){
            AreaVo areaVo = new AreaVo();
            BeanUtils.copyProperties(area, areaVo);
            areaVoList.add(areaVo);
        }
        return areaVoList;
	}


    @Override
    public List<AreaVo> findAreaTree(String countryCode) {
        List<AreaVo> areaVoList = this.tbAreaDao.findByCountryCode(countryCode);
        if (areaVoList!=null){
            return buildTree(0, areaVoList);
        }
        return null;
    }


    private List<AreaVo> buildTree(Integer parentId, List<AreaVo> areaVoList){
        List<AreaVo> childList = new ArrayList<>();
        for(Iterator<AreaVo> iter = areaVoList.iterator(); iter.hasNext();){	//用iterator可以移除元素
            AreaVo areaVo = iter.next();
            if(parentId.equals(areaVo.getParentId())){		//找到儿子节点
                iter.remove(); 				//从orgzInfoList移除掉
                childList.add(areaVo);	//儿子列表
            }
        }
        //循环完之后，再循环刚刚的儿子列表，找出他们各自的儿子列表,这样应该递归不会太深
        for(AreaVo node  : childList){
            node.setChildren(this.buildTree(node.getId(), areaVoList));
        }
        //log.debug(" areaVoList size ={}", areaVoList.size());
        return childList;
    }

}
